package de.tu_darmstadt.gdi1.xcom.view;

import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
import java.util.Locale;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.ToolTipManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import de.tu_darmstadt.gdi1.framework.events.DefaultUserInterfaceEvent;
import de.tu_darmstadt.gdi1.framework.interfaces.IController;
import de.tu_darmstadt.gdi1.framework.interfaces.IControllerEvent;
import de.tu_darmstadt.gdi1.framework.interfaces.IDictionary;
import de.tu_darmstadt.gdi1.framework.interfaces.IUserInterfaceEvent;
import de.tu_darmstadt.gdi1.framework.utils.Dictionary;
import de.tu_darmstadt.gdi1.framework.utils.FrameworkUtils;
import de.tu_darmstadt.gdi1.framework.utils.Point;
import de.tu_darmstadt.gdi1.framework.view.AboutWindow;
import de.tu_darmstadt.gdi1.framework.view.HelpWindow;
import de.tu_darmstadt.gdi1.framework.view.UserInterface;
import de.tu_darmstadt.gdi1.xcom.controller.XcomController;
import de.tu_darmstadt.gdi1.xcom.controller.events.ApplicationEventEnum;
import de.tu_darmstadt.gdi1.xcom.controller.events.GameStateEnum;
import de.tu_darmstadt.gdi1.xcom.controller.events.UserActionEnum;
import de.tu_darmstadt.gdi1.xcom.controller.events.XcomApplicationEvent;
import de.tu_darmstadt.gdi1.xcom.controller.events.XcomGameStateEvent;
import de.tu_darmstadt.gdi1.xcom.controller.events.XcomUserActionEvent;
import de.tu_darmstadt.gdi1.xcom.model.XcomGameData;
import de.tu_darmstadt.gdi1.xcom.model.elements.GameElement;
import de.tu_darmstadt.gdi1.xcom.model.elements.Human;
import de.tu_darmstadt.gdi1.xcom.model.elements.Mob;
import de.tu_darmstadt.gdi1.xcom.model.elements.Switch;
import de.tu_darmstadt.gdi1.xcom.view.events.UIDefaultUpdateEvent;
import de.tu_darmstadt.gdi1.xcom.view.events.UIStateChangeEvent;

public class XcomUI extends UserInterface<GameElement>{

	/**
	 * 
	 */
	private static final long serialVersionUID = 3340570227328559266L;
	private static final String DICTIONARY_PATH = "de.tu_darmstadt.gdi1.xcom.view.dictionary.dictionary";
	private static final int STD_SIZE = 30;
	/** default file extension for saved games. */
	private static final String STD_SAVED_GAME_FILE_EXTENSION = "sav";
	/** default file extension for single levels. */
	private static final String STD_SINGLE_LEVEL_FILE_EXTENSION = "lvl";
	
	private Tiles tiles;
	private XcomController xComController;
	private JPanel controlPanel;
	private JTabbedPane teamBar;
	private JButton turnEnd;

	private File path;
	private Dictionary dict;
	
	public XcomUI(){
		super();
	}
	public XcomUI(XcomController controller) throws FileNotFoundException{
		super(true, FrameworkUtils.loadFile("resource/menu/menu.xml").getPath(), getPausePanel(controller,
				new Dictionary(DICTIONARY_PATH, Locale.getDefault())), new Dictionary(DICTIONARY_PATH, Locale.getDefault()));
		
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		xComController = controller;
		tiles = new Tiles(xComController);
		dict = new Dictionary(DICTIONARY_PATH, Locale.getDefault());
		
		controlPanel = new JPanel();
		controlPanel.setLayout(new BoxLayout(controlPanel, BoxLayout.X_AXIS));
		turnEnd = new JButton("Zug beenden");
		turnEnd.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				xComController.handleEvent(new XcomUserActionEvent(UserActionEnum.EndTurn));
				
			}
		});
		turnEnd.setFocusable(false);
		JEditorPane ep = new JEditorPane();
	    ep.setEditable(false);
	    ep.setFocusable(false);
	
	    
		teamBar = new JTabbedPane();
			

		controlPanel.add(teamBar);
		controlPanel.add(turnEnd);
		this.add(controlPanel);
		ToolTipManager.sharedInstance().setInitialDelay(0);
		ToolTipManager.sharedInstance().setDismissDelay(10000);
		ToolTipManager.sharedInstance().setReshowDelay(0);
	}

	private JTabbedPane createTeamBar() {
		XcomGameData gd = xComController.getGd();
		JTabbedPane teamBar = new JTabbedPane(JTabbedPane.TOP);
		teamBar.setFocusable(false);
		List<Mob> team = gd.getActiveTeamMobs();
		for(int i = 0; i < team.size();i++){
			Mob m = team.get(i);
			JPanel tab = new JPanel();
			tab.setLayout(new BoxLayout(tab, BoxLayout.Y_AXIS));
			Box healthBox = Box.createHorizontalBox();
			healthBox.add(new JLabel("Lebenspunkte: "));
			healthBox.add(new JLabel("" + m.getHealth()));
			
			Box actionPointsBox = Box.createHorizontalBox();
			actionPointsBox.add(new JLabel("Actionpunkte: "));
			actionPointsBox.add(new JLabel("" + m.getPoints()));
			
			Box directionBox = Box.createHorizontalBox();
			directionBox.add(new JLabel("Richtung: "));
			directionBox.add(new JLabel("" + m.getDirection()));
			
			Box meeleDamageBox = Box.createHorizontalBox();
			meeleDamageBox.add(new JLabel("Nahkampfschaden: "));
			meeleDamageBox.add(new JLabel("" + m.getMeeleDamage()));
			
			
			
			tab.add(healthBox);
			tab.add(actionPointsBox);
			tab.add(directionBox);
			tab.add(meeleDamageBox);
			
			String tabName = "Monster" + (i + 1);
			if(m instanceof Human){
				tabName = "Human" + (i + 1);
				Human temp = (Human)m;
				Box weaponDamageBox = Box.createHorizontalBox();
				weaponDamageBox.add(new JLabel("Wafenschaden: "));
				weaponDamageBox.add(new JLabel("" + temp.getWeaponDamage()));
				
				Box munitionBox = Box.createHorizontalBox();
				munitionBox.add(new JLabel("Munition: "));
				munitionBox.add(new JLabel("" + temp.getMunition()));
				tab.add(weaponDamageBox);
				tab.add(munitionBox);
			}
			teamBar.addTab(tabName, tab);
		}
		if(team.size() > 0)
			teamBar.setSelectedIndex(gd.getActiveTeam().getCurrentMobIndex());
		teamBar.addChangeListener(new ChangeListener() {
			
			@Override
			public void stateChanged(ChangeEvent arg0) {
				JTabbedPane teamBar = (JTabbedPane)arg0.getSource();
				
				XcomUserActionEvent ev = new XcomUserActionEvent(UserActionEnum.ChangeMOB);
				ev.setIntOne(teamBar.getSelectedIndex());
				xComController.handleEvent(ev);
			}
		});
		return teamBar;
	}
	@Override
	protected void handleNonFrameworkEvents(
			IUserInterfaceEvent<GameElement> event) {
		if(event instanceof UIDefaultUpdateEvent || event instanceof DefaultUserInterfaceEvent){
			controlPanel.remove(turnEnd);
			controlPanel.remove(teamBar);
			if(event.getPauseMode() == null || (event.getPauseMode() != null && !event.getPauseMode())){
				teamBar = createTeamBar();
				controlPanel.add(teamBar);
				controlPanel.add(turnEnd);
				setEnemyInformation();
			}
			this.pack();
			
		}
		if(event instanceof UIStateChangeEvent){
			UIStateChangeEvent ev = (UIStateChangeEvent)event;
			handleStateChange(ev.getState());
		}
		
	}

	private void setEnemyInformation() {
		JPanel board = null;
		if(boardPanel.getComponent(0) instanceof JPanel)
			board = (JPanel)boardPanel.getComponent(0);
			
		if(board == null)
			return;
		
		List<Mob> enemies = xComController.getGd().getEnemyTeamMobs();
		
		for(Mob m : enemies){
			Point pos = m.getPosition();
			if(xComController.getVisibilityForField(pos)){
				String enemyInfo = "" + m.getHealth() + " hp";
				setToolTip(pos, enemyInfo);
			}
		}
	}
	
	public void setToolTip(Point pos, String text){
		JPanel board = null;
		if(boardPanel.getComponent(0) instanceof JPanel)
			board = (JPanel)boardPanel.getComponent(0);
			
		if(board == null)
			return;
		
		int index = pos.getX() + boardPanel.getGameColumns() * pos.getY();
		JComponent label = null;
		if(board.getComponent(index) instanceof JLabel)
			label = (JLabel)board.getComponent(index);
		if(label != null){
			label.setToolTipText(text);
		}
	}
	private void handleStateChange(GameStateEnum state) {
		switch(state){
		case GAME_WON:
			JOptionPane.showConfirmDialog(this, "Du hast gewonnen!", "Spiel gewonnen", JOptionPane.CLOSED_OPTION);
			xComController.handleEvent(new XcomApplicationEvent(ApplicationEventEnum.LoadNextLevel));
			break;
		case GAME_LOST:
			JOptionPane.showConfirmDialog(this, "Du hast verloren!", "Spiel verloren", JOptionPane.CLOSED_OPTION);
			xComController.handleEvent(new XcomApplicationEvent(ApplicationEventEnum.RESTART));
		}
		
	}
	@Override
	protected void mouseClicked(int x, int y) {
		XcomUserActionEvent event = new XcomUserActionEvent(UserActionEnum.FieldClicked);
		event.setIntOne(x);
		event.setIntTwo(y);
		xComController.handleEvent(event);
		
	}

	@Override
	protected void keyboardKeyTyped(KeyEvent evt) {
		// nothing to do here
	}

	@Override
	protected void keyboardKeyPressed(KeyEvent e) {
		// nothing to do here
		
	}

	@Override
	protected void keyboardKeyReleased(KeyEvent e) {
		int key = e.getKeyCode();
		// see comment in mouseClicked()..
		XcomUserActionEvent event = new XcomUserActionEvent(UserActionEnum.KeyPressed);
		event.setIntOne(key);
		xComController.handleEvent(event);
	}

	@Override
	public void menuItemClicked(String itemName) {
		IControllerEvent event = null;
		/* GAME MENU */
		if (itemName.equals("new")) { // New
			event = new XcomApplicationEvent(ApplicationEventEnum.NEW);
		}
		if (itemName.equals("restart")) { // New
			event = new XcomApplicationEvent(ApplicationEventEnum.RESTART);
		}
		if (itemName.equals("loadlevel")) { // Load level
			File file = showOpenFileDialog(path, new String[] {STD_SINGLE_LEVEL_FILE_EXTENSION});
			if (file != null) {
				path = file.getParentFile();
				event = new XcomApplicationEvent(ApplicationEventEnum.LOAD);
				event.setString(file.getPath());
			} 
			else {
				return; 
			}
		}
		if (itemName.equals("savelevel")) { // Save game
			File file = showSaveFileDialog(null, new String[] { STD_SINGLE_LEVEL_FILE_EXTENSION });
			if (file != null) {
				event = new XcomApplicationEvent(ApplicationEventEnum.SAVE);
				event.setString(file.getPath());
			} else {
				return;
			}
		} 
		
		if (itemName.equals("mapVis")) { // Save game
			event = new XcomApplicationEvent(ApplicationEventEnum.VISIBILITY);
			//xComController.changeMapVisibility();
		} 
		if (itemName.equals("break")) // Break
			event = new XcomGameStateEvent(GameStateEnum.PAUSED);
		
		if (itemName.equals("continue")) // Continue
			event = new XcomGameStateEvent(GameStateEnum.CONTINUE);
		
		if (itemName.equals("quit")) { // Quit
			this.closingRequested();
			return;
			/* EDIT MENU */
		} 
		/* INFO MENU */

		if (itemName.equals("help")) { // Help
			event = new XcomGameStateEvent(GameStateEnum.PAUSED);
			showHelp();
		} 
		if (itemName.equals("about")) { // About
			event = new XcomGameStateEvent(GameStateEnum.PAUSED);
			
			showAbout();
		}
		// send the event
		if (event == null) {
			System.out.println("Unhandeld menuItemClicked: " + itemName);
		} else {
			xComController.handleEvent(event);
		}
	}

	@Override
	protected void closingRequested() {
		xComController.handleEventImmediately(new XcomApplicationEvent(ApplicationEventEnum.QUIT));
		this.dispose();
	}

	@Override
	public ImageIcon getComponentForList(List<GameElement> aList,
			Point coordinates) {
		ImageIcon image;
		// delegate to sprites to get the image-icon
		if(aList.get(0) instanceof Switch)
			// special case for switches, because here the first switch counts
			image = tiles.getImageIcon(aList.get(0), coordinates);
		else
			image = tiles.getImageIcon(aList.get(aList.size() - 1), coordinates);
		if(coordinates.equals(xComController.getGd().getActiveTeam().getCurrentMob().getPosition()))
			image = tiles.addFocus(image);
		if(xComController.showAttack() && coordinates.equals(xComController.getAttackedField()))
			image = tiles.addAttack(image);
		return image;
	}
	
	private void showHelp() {
		String fileLocation = dict.getLocalizedText("STD_HELP_FILE_LOCATION");
		try {
			HelpWindow helpWindow = new HelpWindow(FrameworkUtils.loadFile(fileLocation));
			helpWindow.addWindowListener(new WindowListener() {
				
				@Override
				public void windowOpened(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowIconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowDeiconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowDeactivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowClosing(WindowEvent arg0) {
					
				}
				
				@Override
				public void windowClosed(WindowEvent arg0) {
					XcomGameStateEvent ev = new XcomGameStateEvent(GameStateEnum.CONTINUE);
					xComController.handleEvent(ev);
				}
				
				@Override
				public void windowActivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
			});
			helpWindow.setVisible(true);
		} catch (IOException e) {
			informUser(dict.getLocalizedText(new String[] {"NO_HELP_FOR_YOU", fileLocation}));
			e.printStackTrace();
		}
	}
	
	private void showAbout() {
		String fileLocation = dict.getLocalizedText("STD_ABOUT_FILE_LOCATION");
		try {
			AboutWindow a = new AboutWindow(FrameworkUtils.loadFile(fileLocation));
			a.setVisible(true);
			
			a.addWindowListener(new WindowListener() {
				
				@Override
				public void windowOpened(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowIconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowDeiconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowDeactivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				
				@Override
				public void windowClosing(WindowEvent arg0) {
					
				}
				
				@Override
				public void windowClosed(WindowEvent arg0) {
					XcomGameStateEvent ev = new XcomGameStateEvent(GameStateEnum.CONTINUE);
					xComController.handleEvent(ev);
				}
				
				@Override
				public void windowActivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
			});
		} catch (IOException e) {
			informUser(dict.getLocalizedText(new String[] {"NO_ABOUT_WINDOW", fileLocation}));
			e.printStackTrace();
		}
	}
	
	/**
	 * Displays an error dialog with the given message and logs the error.
	 * 
	 * @param message
	 */
	private void informUser(final String message) {
		JOptionPane.showMessageDialog(null, message, dict.getLocalizedText("Error"),
				JOptionPane.ERROR_MESSAGE);
	}

	protected static JPanel getPausePanel(final IController controller, final IDictionary dictionary) {
		JPanel panel = new JPanel();
		JLabel label = new JLabel(dictionary.getLocalizedText("STR_PAUSED"));
		label.setFont(new Font(Font.SANS_SERIF, Font.BOLD, STD_SIZE));
		JButton button = new JButton();
		button.add(new JLabel(dictionary.getLocalizedText("STR_CONTINUE")));
		button.addMouseListener(new MouseAdapter() {
			public void mousePressed(final MouseEvent evt) {
				controller.handleEvent(new XcomGameStateEvent(GameStateEnum.RUNNING));
			}
		});
		panel.add(label);
		return panel;
	}
	
	
}
